﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;

namespace Microsoft.TimerService
{
    /// <summary>
    /// 服务定时器管理
    /// </summary>
    public abstract class ServiceTimerControl
    {
        #region 私有成员
        /// <summary>
        /// 定时器
        /// </summary>
        private Timer SysTimer { get; set; }
        /// <summary>
        /// 是否启用定时器
        /// </summary>
        private bool _EnabledTimer = true;
        /// <summary>
        /// 定时器配置
        /// </summary>
        ServiceTimeConfig Config;
        #endregion

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            _EnabledTimer = false;

            SysTimer.Change(Timeout.Infinite, Timeout.Infinite);
        }
        /// <summary>
        /// 开始服务
        /// </summary>
        public void Start()
        {
            _EnabledTimer = true;
            Config = this.GetTimerConfig();

            SysTimer = new Timer(new TimerCallback(this.TimerProcess), AppDomain.CurrentDomain, 0, this.Config.ChkInterval);
        }
        /// <summary>
        /// 处理间隔服务
        /// </summary>
        /// <param name="sender"></param>
        private void TimerProcess(object sender)
        {
            if (!_EnabledTimer) return;

            TimerControl cft = null;
            bool TimeIsUp = true;
            if (this.Config.TimerMode != TimerMode.Interval)
            {
                // 如果定时方式不是定时轮询的话，就构造TimerControl类，该类用来计算每次执行完程序后
                // 到下次执行服务时需要休眠的时间
                cft = new TimerControl(this.Config.StartTime, this.Config.TimerMode);
                TimeIsUp = cft.TimeIsUp;	// 获取是否到了执行服务程序的时间了
            }

            try
            {
                if (TimeIsUp)// 时间到了可以执行程序了
                {
                    // 设置计时器，在无穷时间后再启用（实际上就是永远不启动计时器了--停止计时器计时）
                    SysTimer.Change(Timeout.Infinite, this.Config.ChkInterval);

                    //开始处理服务
                    this.StartService();
                }
            }
            catch (Exception ex) { this.ServiceException(ex); }	// 处理服务执行过程中出现的异常
            finally
            {
                // 如果计时器不为空，则重新设置休眠的时间
                if (SysTimer != null)
                {
                    if (this.Config.TimerMode == TimerMode.Interval)// 定时轮询设置
                    {
                        // 重新启用计时器
                        SysTimer.Change(this.Config.ChkInterval, this.Config.ChkInterval);
                    }
                    else// 定时设置
                    {
                        // 用cft类计算下一次到期的时间
                        TimeSpan Interval = cft.GetNextTimeUp();
                        // 重新启用计时器
                        SysTimer.Change(Interval, Interval);
                    }
                }
            }
        }
        /// <summary>
        /// 开始服务
        /// </summary>
        protected abstract void StartService();
        /// <summary>
        /// 定时器初始化
        /// </summary>
        /// <param name="TimerMode"></param>
        /// <param name="StartTime"></param>
        /// <param name="ChkInterval"></param>
        protected abstract ServiceTimeConfig GetTimerConfig();
        /// <summary>
        /// 系统服务错误
        /// </summary>
        /// <param name="ex"></param>
        protected virtual void ServiceException(Exception ex) { return; }
    }

    #region 系统配置实体类

    /// <summary>
    /// 服务定时器配置
    /// </summary>
    public class ServiceTimeConfig
    {
        /// <summary>
        /// 轮询目录时间间隔（单位：毫秒）
        /// </summary>
        public int ChkInterval { get; set; }
        /// <summary>
        /// StartTime值为：
        /// 
        /// TimerMode=TimerMode.Month	"09|04:30"	-表示每月9号的凌晨4点30分
        /// TimerMode=TimerMode.Week	 "0|04:30"	-表示每星期天的4点30分
        /// TimerMode=TimerMode.Week	 "6|04:00"	-表示每星期6的4点30分
        /// TimerMode=TimerMode.Day		  "|04:00"	-表示每天的4点00分
        /// TimerMode=TimerMode.Date "08-10|04:00"	-表示每年8月10号的4点00分执行一次
        /// TimerMode=TimerMode.Year   "246|04"		-表示每年第246天的4点00分执行一次（可以不填写分钟默认为00）
        /// </summary>
        public string StartTime { get; set; }
        /// <summary>
        /// 服务器定时处理模型
        /// </summary>
        public TimerMode TimerMode { get; set; }
    }
    /// <summary>
    /// 服务处理方法
    /// </summary>
    public enum TimerMode
    {
        /// <summary>
        /// 轮询方式
        /// </summary>
        Interval = 0,
        /// <summary>
        /// 一个月中某个天数的指定时间
        /// </summary>
        Month = 1,
        /// <summary>
        /// 一周中的周几的指定时间
        /// </summary>
        Week = 2,
        /// <summary>
        /// 一天中的指定时间
        /// </summary>
        Day = 3,
        /// <summary>
        /// 一年中第几天的指定时间
        /// </summary>
        Year = 4,
        /// <summary>
        /// 一年中的指定日期的指定时间
        /// </summary>
        Date = 5
    }
    #endregion

    #region 定时服务休眠计算类
    /// <summary>
    /// 文件生成时间配置
    /// </summary>
    public class TimerControl
    {
        #region 私有成员
        /// <summary>
        /// 间隔单位
        /// </summary>
        private TimerMode type;
        /// <summary>
        /// 月份
        /// </summary>
        private int Month;
        /// <summary>
        /// 天
        /// </summary>
        private int Day;
        /// <summary>
        /// 小时
        /// </summary>
        private int Hour;
        /// <summary>
        /// 分钟
        /// </summary>
        private int Minute = 0;
        #endregion

        #region 公共成员方法
        /// <summary>
        /// StartTime值为：
        /// 
        /// TimerMode=TimerMode.Month	"09|04:30"	-表示每月9号的凌晨4点30分
        /// TimerMode=TimerMode.Week	 "0|04:30"	-表示每星期天的4点30分
        /// TimerMode=TimerMode.Week	 "6|04:00"	-表示每星期6的4点30分
        /// TimerMode=TimerMode.Day		  "|04:00"	-表示每天的4点00分
        /// TimerMode=TimerMode.Date "08-10|04:00"	-表示每年8月10号的4点00分执行一次
        /// TimerMode=TimerMode.Year   "246|04"		-表示每年第246天的4点00分执行一次（可以不填写分钟默认为00）
        /// </summary>
        public TimerControl(string StartTime, TimerMode timeMode)
        {
            //Regex regEx = new Regex( @"(?<Type>[MWDY])(?<Days>/d+)?/|(?<Hour>/d+):?(?<Minute>[0-5]/d?)?", RegexOptions.Compiled | RegexOptions.IgnoreCase );
            Regex regEx = new Regex(@"^(?:(?<Month>[0]?/d|1[0-2])-)?(?<Days>/d{1,3})?/|(?<Hour>[01]?/d|2[0-3])(?::(?<Minute>[0-5]/d?))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

            this.type = timeMode;

            Match m = regEx.Match(StartTime);
            if (m.Success)
            {
                if (String.IsNullOrEmpty(m.Groups["Month"].Value) && this.type == TimerMode.Date)
                    throw new Exception("定时器时间配置异常！");

                if (!String.IsNullOrEmpty(m.Groups["Month"].Value))
                    this.Month = Convert.ToInt32(m.Groups["Month"].Value);

                if (!String.IsNullOrEmpty(m.Groups["Days"].Value))
                    this.Day = Convert.ToInt32(m.Groups["Days"].Value);

                this.Hour = Convert.ToInt32(m.Groups["Hour"].Value);

                if (!String.IsNullOrEmpty(m.Groups["Minute"].Value))
                    this.Minute = Convert.ToInt32(m.Groups["Minute"].Value);
            }
            else
                throw new Exception("定时器时间配置异常！");
        }
        /// <summary>
        /// 判断时间是否到了
        /// </summary>
        /// <returns></returns>
        public bool TimeIsUp
        {
            get
            {
                DateTime dt = DateTime.Now;
                switch (type)
                {
                    case TimerMode.Day:
                        return (dt.Hour == this.Hour && dt.Minute == this.Minute);
                    case TimerMode.Month:
                        return (dt.Day == this.Day && dt.Hour == this.Hour && dt.Minute == this.Minute);
                    case TimerMode.Week:
                        return (((int)dt.DayOfWeek) == this.Day && dt.Hour == this.Hour && dt.Minute == this.Minute);
                    case TimerMode.Date:
                        return (dt.Month == this.Month && dt.Day == this.Day && dt.Hour == this.Hour && dt.Minute == this.Minute);
                    case TimerMode.Year:
                        return (dt.DayOfYear == this.Day && dt.Hour == this.Hour && dt.Minute == this.Minute);
                }
                return false;
            }
        }
        /// <summary>
        /// 从现在起到下次时间到还有多少时间
        /// </summary>
        /// <returns></returns>
        public TimeSpan GetNextTimeUp()
        {
            ///目标时间
            DateTime dt = DateTime.Now;
            DateTime now, target;
            switch (this.type)
            {
                case TimerMode.Day:
                    #region 每天指定某时执行一次
                    now = new DateTime(1, 1, 1, dt.Hour, dt.Minute, 0);
                    target = new DateTime(1, 1, 1, this.Hour, this.Minute, 0);
                    if (now.Ticks >= target.Ticks) dt = dt.AddDays(1.0);	//如果当前时间小于指定时刻，则不需要加天

                    dt = new DateTime(dt.Year, dt.Month, dt.Day, this.Hour, this.Minute, 0);
                    #endregion
                    break;
                case TimerMode.Month:
                    #region 每月指定某天某时执行一次
                    now = new DateTime(1, 1, dt.Day, dt.Hour, dt.Minute, 0);
                    target = new DateTime(1, 1, this.Day, this.Hour, this.Minute, 0);
                    if (now.Ticks >= target.Ticks) dt = dt.AddMonths(1);

                    dt = new DateTime(dt.Year, dt.Month, this.Day, this.Hour, this.Minute, 0);
                    #endregion
                    break;
                case TimerMode.Week:
                    #region 每星期指定星期某时执行一次
                    int dow = (int)dt.DayOfWeek;
                    now = new DateTime(1, 1, dow + 1, dt.Hour, dt.Minute, 0);
                    target = new DateTime(1, 1, this.Day + 1, this.Hour, this.Minute, 0);

                    if (now.Ticks >= target.Ticks)
                        dt = dt.AddDays(this.Day - dow + 7);
                    else
                        dt = dt.AddDays(this.Day - dow);

                    dt = new DateTime(dt.Year, dt.Month, dt.Day, this.Hour, this.Minute, 0);
                    #endregion
                    break;
                case TimerMode.Date:
                    #region 每年指定某月某日某时执行一次
                    now = new DateTime(1, dt.Month, dt.Day, dt.Hour, dt.Minute, 0);
                    target = new DateTime(1, this.Month, this.Day, this.Hour, this.Minute, 0);
                    if (now.Ticks >= target.Ticks) dt = dt.AddYears(1);

                    dt = new DateTime(dt.Year, this.Month, this.Day, this.Hour, this.Minute, 0);
                    #endregion
                    break;
                case TimerMode.Year:
                    #region 每年指定第N天某时执行一次
                    now = new DateTime(1, 1, 1, dt.Hour, dt.Minute, 0);
                    target = new DateTime(1, 1, 1, this.Hour, this.Minute, 0);
                    if (dt.DayOfYear > this.Day || dt.DayOfYear == this.Day && now.Ticks >= target.Ticks) dt = dt.AddYears(1);
                    dt = dt.AddDays(this.Day - dt.DayOfYear);

                    dt = new DateTime(dt.Year, dt.Month, dt.Day, this.Hour, this.Minute, 0);
                    #endregion
                    break;
                default:
                    throw new Exception("定时器时间配置异常！");
            }

            TimeSpan NextCrtFileTime = dt - DateTime.Now;

            if ((int)NextCrtFileTime.TotalMilliseconds > int.MaxValue)	// 如果要休眠的时间间隔超过int类型可以表示的毫秒时，先休眠到int.MaxValue,然后再次休眠
                NextCrtFileTime = new TimeSpan(int.MaxValue);

            return NextCrtFileTime;
        }
        #endregion
    }
    #endregion
}

